package com.cypher.encryptionanddecryption.bluetooth;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.core.content.ContextCompat;

import com.cypher.encryptionanddecryption.R;
import com.cypher.encryptionanddecryption.activity.MainActivity;
import com.cypher.encryptionanddecryption.adapter.BluetoothTalkAdapter;
import com.cypher.encryptionanddecryption.util.BluetoothBleUuidUtils;
import com.cypher.encryptionanddecryption.util.HexUtils;
import com.cypher.encryptionanddecryption.util.ProtocolUtils;

import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 蓝牙连接管理类
 * @author: 古霸天
 * @date: 2020/11/6
 */
public class BluetoothGattManager{
    // 要连接的蓝牙设备
    private BluetoothDevice device;

    /**
     *  GATT客户端
     */
    private BluetoothGatt bluetoothGatt;
    /**
     *  GATT写入服务
     */
    private BluetoothGattService writeService;
    /**
     * GATT写入特征
     */
    private BluetoothGattCharacteristic writeCharacteristic;
    /**
     * GATT读取服务
     */
    private BluetoothGattService notifyService;
    /**
     * GATT读取特征
     */
    private BluetoothGattCharacteristic notifyCharacteristic;
    public BluetoothGatt getBluetoothGatt() {
        return bluetoothGatt;
    }
    public void setBluetoothGatt(BluetoothGatt bluetoothGatt) {
        this.bluetoothGatt = bluetoothGatt;
    }
    public BluetoothGattService getWriteService() {
        return writeService;
    }
    public void setWriteService(BluetoothGattService writeService) {
        this.writeService = writeService;
    }
    public BluetoothGattCharacteristic getWriteCharacteristic() {
        return writeCharacteristic;
    }
    public void setWriteCharacteristic(BluetoothGattCharacteristic writeCharacteristic) {
        this.writeCharacteristic = writeCharacteristic;
    }
    public BluetoothGattService getNotifyService() {
        return notifyService;
    }
    public void setNotifyService(BluetoothGattService notifyService) {
        this.notifyService = notifyService;
    }
    public BluetoothGattCharacteristic getNotifyCharacteristic() {
        return notifyCharacteristic;
    }
    public void setNotifyCharacteristic(BluetoothGattCharacteristic notifyCharacteristic) {
        this.notifyCharacteristic = notifyCharacteristic;
    }
    private BluetoothUtilManager bluetoothUtilManager;
    private TextView txtTop;
    private Activity activity;
    // 连接按钮标签
    private Map<String, Object> tag;
    // 连接按钮
    private View connectBtn;
    // 主线程中的Handler
    private Handler handler;
    public Handler getHandler() {
        return handler;
    }
    private ListView talkList;
    private BluetoothTalkAdapter talkAdapter;
    public ListView getTalkList() {
        return talkList;
    }
    public void setTalkList(ListView talkList) {
        this.talkList = talkList;
    }
    public BluetoothTalkAdapter getTalkAdapter() {
        return talkAdapter;
    }
    public void setTalkAdapter(BluetoothTalkAdapter talkAdapter) {
        this.talkAdapter = talkAdapter;
    }
    /**
     * 修改后的MTU
     */
    private boolean isLargeMtu;
    /**
     * 蓝牙是否连接
     */
    private boolean isGattConnected = false;
    public boolean isGattConnected() {
        return isGattConnected;
    }
    public void setGattConnected(boolean gattConnected) {
        isGattConnected = gattConnected;
    }

    /**
     * 接收数据是否合法
     */
    private boolean receiveData;
    public boolean isReceiveData() {
        return receiveData;
    }
    public void setReceiveData(boolean receiveData) {
        this.receiveData = receiveData;
    }

    /**
     * 存放接收数据缓存区
     */
    private byte[] buf;
    public byte[] getBuf() {
        return buf;
    }

    /**
     * 临时存放接收数据缓存区
     */
    private byte[] tmpBuf;

    /**
     * 未接收完，默认为否
     */
    private boolean notReceived = false;
    /**
     * 设置未接收完
     * @param notReceived
     */
    public void setNotReceived(boolean notReceived) {
        this.notReceived = notReceived;
    }

    /**
     * 初始化蓝牙连接管理类
     * @param connectBtn
     */
    public BluetoothGattManager(View connectBtn) {
        this.bluetoothUtilManager = BluetoothManagerSingleton.getInstance().getBluetoothUtilManager();
        activity = bluetoothUtilManager.getActivity();
        txtTop = activity.findViewById(R.id.txt_top);

        handler = bluetoothUtilManager.getHandler();

        this.connectBtn = connectBtn;
        // 获取连接蓝牙按钮标签
        this.tag = (Map<String, Object>) connectBtn.getTag();

        this.device = bluetoothUtilManager.getDevices().get(Integer.valueOf(tag.get("position").toString()));

        txtTop.setText("正在连接...");

        connect();
    }

    /**
     * 开始连接
     * @param connectBtn
     */
    public void startGatt(View connectBtn) {
        // 关闭之前连接，释放资源
        closeGatt(this.connectBtn);
        // 修改按钮为当前按钮
        this.connectBtn = connectBtn;
        tag = (Map<String, Object>) connectBtn.getTag();
        // 获取该设备
        this.device = bluetoothUtilManager.getDevices().get(Integer.valueOf(tag.get("position").toString()));
        txtTop.setText("正在连接...");
        // 开始连接
        connect();
    }

    /**
     * 关闭连接
     */
    public void closeGatt(View connect) {
        txtTop.setText("正在关闭...");
        if(bluetoothGatt != null) {
            // 断开连接
            bluetoothGatt.disconnect();
            // 释放资源
            bluetoothGatt.close();
            // 关闭蓝牙设置蓝牙是否连接成功
            isGattConnected = false;
            // 异步线程采用主线程handler修改
            Runnable runnable = () -> {
                // 如果状态为断开连接 则返回
                Map<String, Object> tagMap = (Map<String, Object>) connect.getTag();
                // 修改状态为连接状态
                tagMap.put("state", "connect");
                connect.setBackground(ContextCompat.getDrawable(connect.getContext(), R.drawable.item_button_style));
                connect.setTag(tagMap);
            };
            handler.post(runnable);
            txtTop.setText("已断开连接");
        }
    }

    /**
     * 修改连接后的状态
     */
    private void updateDisplay(int style, String state, String topTxt) {
        // 保证线程同步进行
        // 异步线程采用主线程handler修改
        Runnable runnable = () -> {
            // 修改背景为断开连接图片
            this.connectBtn.setBackground(ContextCompat.getDrawable(this.connectBtn.getContext(), style));
            // 修改状态为断开连接
            this.tag.put("state", state);
            this.connectBtn.setTag(this.tag);
            txtTop.setText(topTxt);
        };
        handler.post(runnable);
    }
    /**
     * 建立蓝牙连接
     */
    public void connect() {
        if(device != null) {
            bluetoothGatt = device.connectGatt(activity, false, new BluetoothGattCallback() {
                /**
                 * 连接状态改变时回调
                 * @param gatt
                 * @param status
                 * @param newState
                 */
                @Override
                public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {


                    if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
                        // 连接成功
                        gatt.discoverServices();
                        isGattConnected = true;
                        String deviceName = StringUtils.isEmpty(device.getName()) ? "未知设备" : device.getName();
                        updateDisplay(R.drawable.item_button_disconnect_style, "disconnect", deviceName + "已连接");
                        // 设置数据发送大小
                        gatt.requestMtu(512);
                        // 程序休眠200毫秒
                        if (!isLargeMtu) {
                            try {
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }


                    } else {
                        // 连接失败
                        bluetoothGatt.close();
                        isGattConnected = false;
                        updateDisplay(R.drawable.item_button_style, "connect", "连接失败");
                    }
                }

                /**
                 * 发现设备（真正开始建立连接）
                 * @param gatt
                 * @param status
                 */
                @Override
                public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                    super.onServicesDiscovered(gatt, status);
                    // 通过UUID找到写入数据服务
                    writeService = gatt.getService(UUID.fromString(BluetoothBleUuidUtils.BLE_WRITE_SERVICE));
                    if(writeService != null) {
                        // 找到写入数据的特征值
                        writeCharacteristic = writeService.getCharacteristic(UUID.fromString(BluetoothBleUuidUtils.BLE_WRITE_CHARACTERISTIC));
                    }

                    // 通过UUID找到订阅消息服务
                    notifyService = gatt.getService(UUID.fromString(BluetoothBleUuidUtils.BLE_NOTIFY_SERVICE));
                    if(notifyService != null) {
                        // 找到订阅消息的特征值
                        notifyCharacteristic = notifyService.getCharacteristic(UUID.fromString(BluetoothBleUuidUtils.BLE_NOTIFY_CHARACTERISTIC));

                        // 订阅通知信息
                        gatt.setCharacteristicNotification(notifyCharacteristic, true);
                        gatt.readCharacteristic(notifyCharacteristic);
                    }
                }

                /**
                 * 收到硬件返回的数据时回调
                 * @param gatt
                 * @param characteristic
                 */
                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    BluetoothGattManager.this.onCharacteristicChanged(characteristic);
                }

                /**
                 * MTU大小改变回调
                 * @param gatt
                 * @param mtu
                 * @param status
                 */
                @Override
                public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
                    super.onMtuChanged(gatt, mtu, status);
                    // MTU修改成功
                    if (status == BluetoothGatt.GATT_SUCCESS && mtu == 512) {
                        isLargeMtu = true;
                        Log.d("tag", "mtu update success: " + mtu);
                    }
                    Log.d("tag", "mtu = " + mtu);
                }
            });
        }
    }

    /**
     * 处理蓝牙接收数据
     * @param characteristic
     */
    public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
        // 接收成功
        byte[] result = characteristic.getValue();
        // 清空buf
        buf = null;
        // 如果接收到新数据，默认校验数据为否
        receiveData = false;
        if (result.length > 0) {
            addList("-> " + HexUtils.formatHex(HexUtils.bytesToHex(result)));
            // 包没有接收完，进行组包处理
            if (notReceived) {
                // 组包
                tmpBuf = ProtocolUtils.setOfPackages(tmpBuf, result);
            } else {
                tmpBuf = result;
            }
            // 校验数据是否合法
            if (ProtocolUtils.checkReceiveData(tmpBuf, ((MainActivity) activity).getProtocolId())) {
                receiveData = true;
                buf = tmpBuf;
                tmpBuf = null;
                notReceived = false;
            } else {
                notReceived = true;
            }
        }
    }
    /**
     * 向蓝牙发送数据
     * @param content
     */
    public void sendMsg(byte[] content) {
        if (writeCharacteristic != null) {

            // 如果MTU大小未修改
            if (!isLargeMtu) {
                try {
                    // 设置数据发送大小
                    bluetoothGatt.requestMtu(512);
                    // 程序休眠100毫秒，先修改MTU大小
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            bluetoothGatt.setCharacteristicNotification(writeCharacteristic, true);
            // 设置写入内容
            writeCharacteristic.setValue(content);
            // 设置写入类型
            writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
            // 写入数据
            bluetoothGatt.writeCharacteristic(writeCharacteristic);
        }
    }
    /**
     * 向ListView插入数据
     * @param content
     */
    public void addList(String content) {
        // View控件不是线程安全的，所以回调函数需要在handler中执行
        Runnable run = () -> {
            if (talkAdapter != null) {
                talkAdapter.getContents().add(content);
                talkAdapter.notifyDataSetChanged();
            }
        };
        handler.post(run);
    }

    public void clearList() {
        if (talkAdapter != null) {
            talkAdapter.setContents(new ArrayList<String>());
            talkAdapter.notifyDataSetChanged();
        }
    }

    /**
     * 校验数据是否合法
     * @return
     */
    public boolean checkData(String tip, Activity activity) {
        // 数据校验
        if (isReceiveData()) return true;
        // 如果一秒内还为校验成功，则为超时
        long time = new Date().getTime();
        while(true) {
            // 校验成功
            if (isReceiveData()) return true;

            long runTime = new Date().getTime();
            // 超时
            if (runTime - time >= 10000) {
                // 设置未接收完未否，蓝牙接收失败
                setNotReceived(false);
                if (!StringUtils.isEmpty(tip)) {
                    Toast.makeText(activity, tip, Toast.LENGTH_SHORT).show();
                }
                return false;
            }
        }
    }

    /**
     * 根据传的状态回到列表顶部或底部
     * 顶部：top
     * 底部：bot
     * 其他情况默认为回到底部
     * @param status
     */
    public void toTopOrBotList(String status) {
        if (talkList != null) {
            if (StringUtils.equals("top", status))
                talkList.setSelection(0);
            else
                talkList.setSelection(talkList.getBottom());
        }
    }

    public void destroyThis() {
        // 断开连接
        bluetoothGatt.disconnect();
        // 释放资源
        bluetoothGatt.close();
        try {
            // 调用垃圾回收
            this.finalize();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}
